//
//  zen_font.hpp
//  font
//
//  Created by MeherTj on 2021/7/7.
//  Copyright © 2021 lover. All rights reserved.
//

#pragma once

#include <vector>
#include <map>
#include <memory>
#include "zen_truetype.h"

#include "zen_exception.h"
#include "zen_image.h"
#include "zen_cache.h"
#include "zen_point.h"

namespace Zen
{
    struct FontChar
    {
        uint32_t unicode = 0;
        std::vector<uint8_t> bitmap;

        int width = 0;
        int height = 0;

        int bitmap_x = 0; /// move x. (left:-)
        int bitmap_y = 0; /// move ? from baseline. (top:-)

        int x_advance = 0; /// areaRect width.
        int y_advance = 0; /// line height.
    };

    struct FontMetrics
    {
        float height = 0;
        float ascent = 0;
        float line_height = 0;
    };

    class Font
    {
      protected:
        Font() = default;
        virtual ~Font() = default;

      public:
        virtual std::shared_ptr<FontChar> LoadCharacter(uint32_t unicode, size_t height) = 0;

        virtual FontMetrics const& GetFontInfo() = 0;

      public:
        static std::shared_ptr<Font> Generate(std::vector<uint8_t> const& data);
    };

    class FontBrush
    {
      public:
        static std::shared_ptr<FontBrush> Generate(std::shared_ptr<Font> font, size_t height);

        virtual std::shared_ptr<FontChar> GetCharBitmap(uint32_t unicode) = 0;

        virtual std::shared_ptr<Font> GetFont() const = 0;

        virtual FontMetrics const& GetBrushInfo() const = 0;

        virtual void ReleaseCache() = 0;

        virtual size_t GetHeight() = 0;

      public:
        virtual ~FontBrush() = default;

      protected:
        FontBrush() = default;
    };

    class FontCache : protected Cache<std::string, Font>
    {
      public:
        static FontCache* Instance();

        void CacheFont(std::string const& name, std::shared_ptr<Font> font)
        {
            Insert(name, font);
        }

        std::shared_ptr<Font> GetFont(std::string const& name)
        {
            return At(name);
        }

        void ClearAllFonts(bool with_used = true)
        {
            Clear(with_used);
        }
    };
} // namespace Zen

namespace Zen
{
    struct FontStyle
    {
        float Alignment = 0.f; // [0., 1.] 0:left, 1:right
        int WordSpace = 0; // [-,+] px as font-size
        int LineSpace = 0; // [-,+] px
        int WidthLimit = 0; // [0, +] 0:nolimit, >0: px
        /*
		@var underline pos
		range: [-,+] in px,
		if UnderLineShift
		< 0, the UNDERLINE will be ABOVE base line.
		= 0, on base line.
		> 0, BELOW bese line.
		 */
        int UnderLineSize = 0;
        int UnderLineShift = 0;
    };

    class FontRender
    {
      public:
        /**
		 @struct RenderSetting
		 @var x_edge, y_edge
		 */
        struct RenderSetting
        {
            int x_off = 0; /// [-,+] move text l->r
            int y_off = 0; /// [-,+] move text top->bot
            int underline = 0;
            uint8_t fill = 0; /// bg_fill.
        };

        /// record a line
        struct LineInfo
        {
            int lefts_limit = 0; // most left pixel pos.
            int right_limit = 0;
            std::vector<std::shared_ptr<FontChar>> chars;
        };

      public:
        static std::shared_ptr<FontRender> Generate(std::shared_ptr<FontBrush>, std::u32string const& text, FontStyle const& style);

        virtual std::shared_ptr<Image> RenderImage(size_t width, size_t height, P2i) = 0;

        virtual size_t Width() = 0;

        virtual size_t Height() = 0;

        virtual size_t GetLineCount() = 0;

        virtual std::vector<LineInfo> const& GetLines() = 0;
    };

    /// tile map
#if 0
	class FontTileMap {
	public:
		struct CharInfo {
			int x, y, w, h;
			float x_off, y_off;
		};

		/// record a line
		struct LineInfo
		{
			int width = 0;
			std::vector<std::shared_ptr<FontChar> > chars;
		};
	public:
		static std::shared_ptr<FontTileMap> Generate
		(std::shared_ptr<FontBrush>, std::u32string const & text, size_t x_count);

		virtual std::shared_ptr<Image> RenderImage(size_t width, size_t height) = 0;

		virtual size_t width() = 0;

		virtual size_t height() = 0;

		virtual size_t GetLineCount() = 0;

		virtual std::vector<CharInfo> const & GetCharInfos() = 0;

		virtual std::vector<LineInfo> const & GetLines() = 0;
	};
#endif
} // namespace Zen
